Sentiment Analysis
Background
I used a variety of sources to help me with this project.
https://bookdown.org/jdholster1/idsr/text-analysis.html
https://www.rdocumentation.org/packages/gutenbergr/versions/0.2.3
https://github.com/ropensci/gutenbergr https://youtu.be/VlBkxoLbgi4?si=0b92BHbiZAX9kvz1 https://datavizs21.classes.andrewheiss.com/example/13-example/
I noticed there was a library that contained all the Harry Potter
volumes in one of the links I referenced. I hoped a library existed for
Edgar Allen Poe. After some searching I did find that someone has done a
similar project using downloads from the Gutenberg Project. This project
uses some of these functions but endeavors to elaborate and analyze the
data more extensively.
https://rpubs.com/abyingt1/887155
The rpub project analyzed a small corpus of Poe’s work which included
only three. My project hope to compare two very different works - one
from Poe and the other from Kate Chopin called The Awakening.
#Libraries In addition to an array of libraries, I included
stop_words into the global environment.
library(devtools)
library(gutenbergr)
library(ggthemes)
library(quanteda)
library(tidyverse)
library(textdata)
library(tidyselect)
library(tidytext)
library(DT)
library(flextable)
library(tm)
library(qdap)
library(wordcloud)
library(wordcloud2)
data("stop_words")
##Gutenbergr
This library contains a catalog literary works maintained by Project
Gutenberg for use. When I have to analyze literature, I try to have a
text copy to write all over and a hard copy edition. In this project I
implemented R Studio as another path towards close textual analysis.
gutenberg_works()
I was curious how many authors were contained in the library. I could
spend time analyzing its contents. For now, we can move on to reading in
some text.
count(gutenberg_authors)
Below I filtered by title to find exactly what I wanted to analyze.
It was important to confirm the correct version and format. I wanted the
English (en) text version. One of the rows is a movie book. I selected
ID 932.
#View(gutenberg_metadata)
gutenberg_metadata %>% 
  filter(title=="The Fall of the House of Usher")
I downloaded the file by ID and saved the download as usher_words in
the global environment. R reads ID 932 as a tibble with two columns and
789 rows. If were to trust the word count the number of words now, I
would be wrong.
gutenberg_download(gutenberg_id = 932) # The Fall Of the House of Usher.
usher_words <- gutenberg_download(932)
texts<-corpus(usher_words)
#view(usher_words)
Using the str function, we can see the structure of what was
downloaded. All the text is nested within itself. This is great, but I
wanted to see each word as its own structure.
getTransformations()
str(usher_words)
Using mutate, each word is given a row number because R unnested
those tokens. After I viewed it, I save it as a new dataframe called
usher1. It is important to note that R also made the text lowercase and
removed any punctuation.
usher1 <- usher_words %>% 
  mutate(line =row_number()) %>% 
unnest_tokens(word, text) %>% 
anti_join(stop_words)
#view(usher1)
Calling usher1 and counting the words now shows the number of words
has increased to 7208. After I go back and add the stop words, I end up
with 1740.
usher1 %>% 
  count(word, sort = TRUE)
I am interested how these words breakdown. Are there any that I could
discount from my analysis using the stopwords list. Which words are used
in abundance? Which are used sparingly? Which words repeat, not only as
the same word, but also as a synonym. How many different words does Poe
use to describe the body or illness? What are the unfamiliar words?
Below I use R to count the words and sort top 100 used words.
usher1 %>% 
count(word, sort = TRUE) %>% 
  filter(n>8) %>% 
  mutate(word = reorder(word,n)) %>% 
  ggplot(aes(word,n)) +
  geom_col(fill="tomato")+
  xlab(NULL)+
  coord_flip()+
  theme_few()
Hoping to see these words more clearly on a plot, I call freq_terms
from the qdap library. To become familiar with the library, I try to
plot words that at least eight characters after I found the top four
frequent terms.
freq_terms(usher1,25)
#stopwords
There are three resources R can call to manage stopwords. Often
people say that some words are not important which is rather
presumptive. Depending on what a person is trying to understand about a
text, all words are important until they are not. For instance, in
Usher, we have to understand the story is told in the first person. The
concept of ‘I’ is very important to the meaning of the story. Before I
apply stopwords, I retained pronouns such as I and his, but after I
applied the stopwords, I had to consider the amount of depth I lose
because pronouns and verb tense was exempt from my analysis after
running that chunk.
AFINN Finn Arup Nielsen bing Bing Lie and collaborators nrc Saif
Mohammed and Puter Turney
usher1 %>% 
  anti_join(stop_words) %>% 
  count(word, sort = TRUE)
qdap has a fun frequency terms function. Below I found all the words
with at least 11 characters (tokens) and plotted the number of times
they appeared in the text.
freq <- freq_terms(usher1$word,at.least =11, stopwords = stop_words)
plot(freq)
The following analysis considers the importance of giving value to
words. I join usher1 with the afinn lexicon to derive the sentiments of
words in the text. I chose the top 30 occurrences.Using the stopwords,
my first visualization lists terror as the most frequent word under the
umbrella of negative sentiment.
usher1 %>% 
  inner_join(get_sentiments('afinn')) %>% 
  arrange(desc(value)) -> usher_sentiment
usher1 %>% 
   anti_join(stop_words) %>% 
  inner_join(get_sentiments('afinn')) %>% 
     filter(value > 0) %>% 
   count(word,  value,sort = TRUE)  -> usher_negative
usher1 %>% 
  anti_join(stop_words) %>% 
  inner_join(get_sentiments("afinn")) %>% 
  filter(value <= 0) %>% 
  count(word, value, sort = TRUE)%>% 
  head(30) %>% 
  ggplot(aes(reorder(word, n), n, fill = value)) + geom_col() + coord_flip() +
   scale_fill_gradient(low = "black", high = "orange")+
  ylab("Number of Occurrences") +
      xlab("Words") + ggtitle("The Fall of the House of Usher Negative Sentiment using stopwords")
However, if I disregard the stopwords, the most recurring word is no
and by a considerable amount.
usher1 %>% 
  inner_join(get_sentiments("afinn")) %>% 
  filter(value <= 0) %>% 
  count(word, value, sort = TRUE)%>% 
  head(30) %>% 
  ggplot(aes(reorder(word, n), n, fill = value)) + geom_col() + coord_flip() +
  scale_fill_gradient(low = "black", high = "orange")+
  ylab("Number of Occurrences") +
      xlab("Words") + ggtitle("The Fall of the House of Usher Negative Sentiment including stop_words")
Conversely, if we consider the positive words we experience the same
conundrum. Implementing stopwords removed words such as certain, good,
best, and great.
usher1 %>% 
   anti_join(stop_words) %>% 
  inner_join(get_sentiments('afinn')) %>% 
     filter(value > 0) %>% 
   count(word,  value,sort = TRUE)  -> usher_positive
usher_positive %>% 
  head(30) %>% 
  ggplot(aes(reorder(word, n,),n, fill = value)) + geom_col() + coord_flip() +
  scale_fill_gradient(low = "gray", high = "orange")+ylab("Number of Occurrences") +
      xlab("Words") + ggtitle("The Fall of the House of Usher Positive Sentiment")
usher1 %>% 
  inner_join(get_sentiments('afinn')) %>% 
     filter(value > 0) %>% 
   count(word,  value,sort = TRUE)  -> usher_positive
usher_positive %>% 
  head(30) %>% 
  ggplot(aes(reorder(word, n,),n, fill = value)) + geom_col() + coord_flip() +
  scale_fill_gradient(low = "gray", high = "orange")+ylab("Number of Occurrences") +
      xlab("Words") + ggtitle("The Fall of the House of Usher Positive Sentiment")
#Wordclouds
The wordcloud can be accomplished using a variety of methods. As with
all things, intent is useful. Why go through the effort to display every
as some sort of design? For some, it might be a useful way to extract
metadata from our own work. Perhaps we can refine our work if it seems
too off the mark of our intended message.
the Worldcloud2 lets you hover over the word and see a count.
wordcloud2(data=usher_negative)
Time to turn my focus to the other text my project, Kate Chopin’s the
Awakening, this text is categorized on the gutenberg_bookshelf as
“Banned Books List from the American Library Association” I tried to
find it using the title but was unable to download ID 100.
View(gutenberg_metadata)
gutenberg_metadata %>% 
  filter(title=="The Awakening")
I went to the site to see if maybe I was spelling something wrong. I
found a version I can download but the story is part of a collection. I
need to do a considerable amount of cleaning before I can try to use
this text.
gutenberg_download(gutenberg_id = 160) # The Awakening.
awakening_words <- gutenberg_download(160, meta_fields = "title")
view(awakening_words)
str(awakening_words)
function to clean text
glimpse(awakening_words)
head(awakening_words)
awakening_clean<-awakening_words %>% 
  slice(80:n()) %>% 
  drop_na(text) %>% 
 unnest_tokens(word,text)
head(awakening_clean)
awakening_clean %>% 
  count(word,sort = TRUE)
awakening_clean %>% 
  count(word,sort = TRUE) %>% 
  filter(n>200) %>% 
  mutate(word = reorder(word,n)) %>% 
  ggplot(aes(word,n))+
  geom_col(fill="springgreen3")+
  xlab(NULL)+
  coord_flip()+
  theme_few()
awakening_bigrams <- awakening_words %>% 
  drop_na(text) %>% 
  # n = 2 here means bigrams. We could also make trigrams (n = 3) or any type of n-gram
  unnest_tokens(bigram, text, token = "ngrams", n = 2) %>% 
  # Get rid of NAs in the new bigram column
  drop_na(bigram) %>% 
  # Split the bigrams into two words so we can remove stopwords
  separate(bigram, c("word1", "word2"), sep = " ") %>% 
  filter(!word1 %in% stop_words$word,
         !word2 %in% stop_words$word) %>% 
  filter(!word1 %in% c("thou", "thy", "thine", "enter", "exeunt", "exit"),
         !word2 %in% c("thou", "thy", "thine", "enter", "exeunt", "exit")) %>% 
  # Put the two word columns back together
  unite(bigram, word1, word2, sep = " ") %>% 
arrange(bigram)
awakening_bigrams
top_bigrams <- awakening_bigrams %>% 
  # Count all the bigrams in each play
  count(title, bigram, sort = TRUE) %>% 
  # Keep top 15 in each play
  group_by(title) %>% 
  top_n(15) %>% 
  ungroup() %>% 
  # Make the bigrams an ordered factor so they plot in order
  mutate(bigram = fct_inorder(bigram))
ggplot(top_bigrams, aes(y = fct_rev(bigram), x = n, fill = title)) + 
  geom_col() + 
  guides(fill = "none") +
  labs(y = "Count", x = NULL, 
       title = "15 most frequent bigrams The Awakening") +
  facet_wrap(vars(title), scales = "free") +
  theme_bw()
log2(8)
2^3
pronouns <- c("he", "she")
bigram_he_she_counts <- awakening_words %>%
  drop_na(text) %>% 
  # Split into bigrams
  unnest_tokens(bigram, text, token = "ngrams", n = 2) %>%
  # Find counts of bigrams
  count(bigram, sort = TRUE) %>%
  # Split the bigram column into two columns
  separate(bigram, c("word1", "word2"), sep = " ") %>%
  # Only choose rows where the first word is he or she
  filter(word1 %in% pronouns) %>%
  count(word1, word2, wt = n, sort = TRUE) %>% 
  rename(total = n)
word_ratios <- bigram_he_she_counts %>%
  # Look at each of the second words
  group_by(word2) %>%
  # Only choose rows where the second word appears more than 10 times
  filter(sum(total) > 10) %>%
  ungroup() %>%
  # Spread out the word1 column so that there's a column named "he" and one named "she"
  spread(word1, total, fill = 0) %>%
  # Add 1 to each number so that logs work (just in case any are zero)
  mutate_if(is.numeric, ~(. + 1) / sum(. + 1)) %>%
  # Create a new column that is the logged ratio of the she counts to he counts
  mutate(logratio = log2(she / he)) %>%
  # Sort by that ratio
  arrange(desc(logratio))
# Rearrange this data so it's plottable
plot_word_ratios <- word_ratios %>%
  # This gets the words in the right order---we take the absolute value, select
  # only rows where the log ratio is bigger than 0, and then take the top 15 words
  mutate(abslogratio = abs(logratio)) %>%
  group_by(logratio < 0) %>%
  top_n(15, abslogratio) %>%
  ungroup() %>%
  mutate(word = reorder(word2, logratio)) 
# Finally we plot this
ggplot(plot_word_ratios, aes(y = word, x = logratio, color = logratio < 0)) +
  geom_segment(aes(y = word, yend = word,
                   x = 0, xend = logratio), 
               size = 1.1, alpha = 0.6) +
  geom_point(size = 3.5) +
  labs(x = "How much more/less likely", y = NULL) +
  scale_color_discrete(name = "", labels = c("More 'she'", "More 'he'")) +
  scale_x_continuous(breaks = seq(-3, 3),
                     labels = c("8x", "4x", "2x",
                                "Same", "2x", "4x", "8x")) +
  theme_bw() +
  theme(legend.position = "bottom")
get_sentiments("afinn")
awakening_clean <- awakening_words %>% 
  drop_na() %>% 
  # Split into word tokens
  unnest_tokens(word, text) %>% 
  # Remove stop words and old timey words
  anti_join(stop_words) %>% 
  filter(!word %in% c("thou", "thy", "haue", "thee", 
                      "thine", "enter", "exeunt", "exit"))
# Join the sentiment dictionary 
awakening_sentiment <- awakening_clean %>% 
  inner_join(get_sentiments("bing"))
awakening_sentiment
awakening_sentiment_plot <- awakening_sentiment %>% 
  count(title, sentiment)
ggplot(awakening_sentiment_plot, aes(x = sentiment, y = n, fill = title, alpha = sentiment)) +
  geom_col(position = position_dodge()) +
  scale_alpha_manual(values = c(0.5, 1))
awakening_split_into_lines <- awakening_sentiment %>% 
  # Divide lines into groups of 100
  mutate(line = row_number(),
         line_chunk = line %/% 100) %>% 
  # Get a count of postive and negative words in each 100-line chunk.
  count(title, line_chunk, sentiment) %>% 
  # Convert the sentiment column into two columns named "positive" and "negative"
  pivot_wider(names_from = sentiment, values_from = n) %>% 
  # Calculate net sentiment
  mutate(sentiment = positive - negative)
ggplot(awakening_split_into_lines,
       aes(x = line_chunk, y = sentiment, fill = sentiment)) +
  geom_col() +
  scale_fill_viridis_c(option = "magma", end = 0.9) +
  facet_wrap(vars(title), scales = "free_x") +
  theme_bw()
I tried to merge both data tables together. However, the challenge is
that I am grabbing a part of the collection from the text I don’t
want.
DT1_2_full <- merge(awakening_sentiment, usher_sentiment, all = TRUE)         # Full join
DT1_2_full                                        # Print data.table
##Conclusions
The Fall of the House of Usher and The Awakening are very different
texts yet they have interesting similarities. Kate Chopin incorporates
Poe’s style in her writing. It is difficult to determine if these are
the same words in both texts, however. Because Kate Chopin’s work is
part of a collection, we need to parse that text we are interested in
from the other texts found in the download.I addition, I noticed that I
was unable to drawn anything conclusive about the texts except for
counts of words and their sentiments. I also believe as a researcher, it
behooves me to create my own list of stop words so I have better control
over what I want to filter.
 
LS0tDQp0aXRsZTogIlJlYWRpbmcgTGl0ZXJhdHVyZSB3aXRoIFIiDQphdXRob3I6ICJDeW50aGlhIEFuc3RleSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjx0aXRsZT5TZW50aW1lbnQgQW5hbHlzaXM8dGl0bGUvPg0KPGgxPkJhY2tncm91bmQ8aDEvPg0KDQpJIHVzZWQgYSB2YXJpZXR5IG9mIHNvdXJjZXMgdG8gaGVscCBtZSB3aXRoIHRoaXMgcHJvamVjdC4gDQoNCmh0dHBzOi8vYm9va2Rvd24ub3JnL2pkaG9sc3RlcjEvaWRzci90ZXh0LWFuYWx5c2lzLmh0bWwNCmh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ndXRlbmJlcmdyL3ZlcnNpb25zLzAuMi4zDQpodHRwczovL2dpdGh1Yi5jb20vcm9wZW5zY2kvZ3V0ZW5iZXJncg0KaHR0cHM6Ly95b3V0dS5iZS9WbEJreG9MYmdpND9zaT0wYjkyQkhiaVpBWDlrdnoxDQpodHRwczovL2RhdGF2aXpzMjEuY2xhc3Nlcy5hbmRyZXdoZWlzcy5jb20vZXhhbXBsZS8xMy1leGFtcGxlLw0KDQoNCkkgbm90aWNlZCB0aGVyZSB3YXMgYSBsaWJyYXJ5IHRoYXQgY29udGFpbmVkIGFsbCB0aGUgSGFycnkgUG90dGVyIHZvbHVtZXMgaW4gb25lIG9mIHRoZSBsaW5rcyBJIHJlZmVyZW5jZWQuIEkgaG9wZWQgYSBsaWJyYXJ5IGV4aXN0ZWQgZm9yIEVkZ2FyIEFsbGVuIFBvZS4gQWZ0ZXIgc29tZSBzZWFyY2hpbmcgSSBkaWQgZmluZCB0aGF0IHNvbWVvbmUgaGFzIGRvbmUgYSBzaW1pbGFyIHByb2plY3QgdXNpbmcgZG93bmxvYWRzIGZyb20gdGhlIEd1dGVuYmVyZyBQcm9qZWN0LiBUaGlzIHByb2plY3QgdXNlcyBzb21lIG9mIHRoZXNlIGZ1bmN0aW9ucyBidXQgZW5kZWF2b3JzIHRvIGVsYWJvcmF0ZSBhbmQgYW5hbHl6ZSB0aGUgZGF0YSBtb3JlIGV4dGVuc2l2ZWx5LiANCg0KaHR0cHM6Ly9ycHVicy5jb20vYWJ5aW5ndDEvODg3MTU1DQoNCg0KVGhlIHJwdWIgcHJvamVjdCBhbmFseXplZCBhIHNtYWxsIGNvcnB1cyBvZiBQb2UncyB3b3JrIHdoaWNoIGluY2x1ZGVkIG9ubHkgdGhyZWUuIE15IHByb2plY3QgaG9wZSB0byBjb21wYXJlIHR3byB2ZXJ5IGRpZmZlcmVudCB3b3JrcyAtIG9uZSBmcm9tIFBvZSBhbmQgdGhlIG90aGVyIGZyb20gS2F0ZSBDaG9waW4gY2FsbGVkIFRoZSBBd2FrZW5pbmcuDQoNCiNMaWJyYXJpZXMNCkluIGFkZGl0aW9uIHRvIGFuIGFycmF5IG9mIGxpYnJhcmllcywgSSBpbmNsdWRlZCBzdG9wX3dvcmRzIGludG8gdGhlIGdsb2JhbCBlbnZpcm9ubWVudC4gDQoNCmBgYHtyfQ0KbGlicmFyeShkZXZ0b29scykNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShndXRlbmJlcmdyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkocXVhbnRlZGEpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGV4dGRhdGEpDQpsaWJyYXJ5KHRpZHlzZWxlY3QpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoZmxleHRhYmxlKQ0KbGlicmFyeSh0bSkNCmxpYnJhcnkocWRhcCkNCmxpYnJhcnkod29yZGNsb3VkKQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0KZGF0YSgic3RvcF93b3JkcyIpDQpgYGANCiMjR3V0ZW5iZXJncg0KDQoNClRoaXMgbGlicmFyeSBjb250YWlucyBhIGNhdGFsb2cgbGl0ZXJhcnkgd29ya3MgbWFpbnRhaW5lZCBieSBQcm9qZWN0IEd1dGVuYmVyZyBmb3IgdXNlLiBXaGVuIEkgaGF2ZSB0byBhbmFseXplIGxpdGVyYXR1cmUsIEkgdHJ5IHRvIGhhdmUgYSB0ZXh0IGNvcHkgdG8gd3JpdGUgYWxsIG92ZXIgYW5kIGEgaGFyZCBjb3B5IGVkaXRpb24uIEluIHRoaXMgcHJvamVjdCBJIGltcGxlbWVudGVkIFIgU3R1ZGlvIGFzIGFub3RoZXIgcGF0aCB0b3dhcmRzIGNsb3NlIHRleHR1YWwgYW5hbHlzaXMuIA0KDQoNCmBgYHtyfQ0KZ3V0ZW5iZXJnX3dvcmtzKCkNCmBgYA0KSSB3YXMgY3VyaW91cyBob3cgbWFueSBhdXRob3JzIHdlcmUgY29udGFpbmVkIGluIHRoZSBsaWJyYXJ5LiBJIGNvdWxkIHNwZW5kIHRpbWUgYW5hbHl6aW5nIGl0cyBjb250ZW50cy4gRm9yIG5vdywgd2UgY2FuIG1vdmUgb24gdG8gcmVhZGluZyBpbiBzb21lIHRleHQuIA0KDQpgYGB7cn0NCmNvdW50KGd1dGVuYmVyZ19hdXRob3JzKQ0KYGBgDQpCZWxvdyBJIGZpbHRlcmVkIGJ5IHRpdGxlIHRvIGZpbmQgZXhhY3RseSB3aGF0IEkgd2FudGVkIHRvIGFuYWx5emUuIEl0IHdhcyBpbXBvcnRhbnQgdG8gY29uZmlybSB0aGUgY29ycmVjdCB2ZXJzaW9uIGFuZCBmb3JtYXQuIEkgd2FudGVkIHRoZSBFbmdsaXNoIChlbikgdGV4dCB2ZXJzaW9uLiBPbmUgb2YgdGhlIHJvd3MgaXMgYSBtb3ZpZSBib29rLiAgSSBzZWxlY3RlZCBJRCA5MzIuIA0KYGBge3J9DQojVmlldyhndXRlbmJlcmdfbWV0YWRhdGEpDQpndXRlbmJlcmdfbWV0YWRhdGEgJT4lIA0KICBmaWx0ZXIodGl0bGU9PSJUaGUgRmFsbCBvZiB0aGUgSG91c2Ugb2YgVXNoZXIiKQ0KYGBgDQpJIGRvd25sb2FkZWQgdGhlIGZpbGUgYnkgSUQgYW5kIHNhdmVkIHRoZSBkb3dubG9hZCBhcyB1c2hlcl93b3JkcyBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50LiBSIHJlYWRzIElEIDkzMiBhcyBhIHRpYmJsZSB3aXRoIHR3byBjb2x1bW5zIGFuZCA3ODkgcm93cy4gSWYgd2VyZSB0byB0cnVzdCB0aGUgd29yZCBjb3VudCB0aGUgbnVtYmVyIG9mIHdvcmRzIG5vdywgSSB3b3VsZCBiZSB3cm9uZy4gDQpgYGB7cn0NCmd1dGVuYmVyZ19kb3dubG9hZChndXRlbmJlcmdfaWQgPSA5MzIpICMgVGhlIEZhbGwgT2YgdGhlIEhvdXNlIG9mIFVzaGVyLg0KdXNoZXJfd29yZHMgPC0gZ3V0ZW5iZXJnX2Rvd25sb2FkKDkzMikNCnRleHRzPC1jb3JwdXModXNoZXJfd29yZHMpDQojdmlldyh1c2hlcl93b3JkcykNCmBgYA0KVXNpbmcgdGhlIHN0ciBmdW5jdGlvbiwgd2UgY2FuIHNlZSB0aGUgc3RydWN0dXJlIG9mIHdoYXQgd2FzIGRvd25sb2FkZWQuIEFsbCB0aGUgdGV4dCBpcyBuZXN0ZWQgd2l0aGluIGl0c2VsZi4gVGhpcyBpcyBncmVhdCwgYnV0IEkgd2FudGVkIHRvIHNlZSBlYWNoIHdvcmQgYXMgaXRzIG93biBzdHJ1Y3R1cmUuDQoNCg0KYGBge3J9DQpnZXRUcmFuc2Zvcm1hdGlvbnMoKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIodXNoZXJfd29yZHMpDQpgYGANClVzaW5nIG11dGF0ZSwgZWFjaCB3b3JkIGlzIGdpdmVuIGEgcm93IG51bWJlciBiZWNhdXNlIFIgdW5uZXN0ZWQgdGhvc2UgdG9rZW5zLiBBZnRlciBJIHZpZXdlZCBpdCwgSSBzYXZlIGl0IGFzIGEgbmV3IGRhdGFmcmFtZSBjYWxsZWQgdXNoZXIxLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IFIgYWxzbyBtYWRlIHRoZSB0ZXh0IGxvd2VyY2FzZSBhbmQgcmVtb3ZlZCBhbnkgcHVuY3R1YXRpb24uIA0KDQoNCmBgYHtyfQ0KdXNoZXIxIDwtIHVzaGVyX3dvcmRzICU+JSANCiAgbXV0YXRlKGxpbmUgPXJvd19udW1iZXIoKSkgJT4lIA0KdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUgDQphbnRpX2pvaW4oc3RvcF93b3JkcykNCiN2aWV3KHVzaGVyMSkNCmBgYA0KQ2FsbGluZyB1c2hlcjEgYW5kIGNvdW50aW5nIHRoZSB3b3JkcyBub3cgc2hvd3MgdGhlIG51bWJlciBvZiB3b3JkcyBoYXMgaW5jcmVhc2VkIHRvIDcyMDguIEFmdGVyIEkgZ28gYmFjayBhbmQgYWRkIHRoZSBzdG9wIHdvcmRzLCBJIGVuZCB1cCB3aXRoIDE3NDAuDQpgYGB7cn0NCnVzaGVyMSAlPiUgDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQ0KYGBgDQpJIGFtIGludGVyZXN0ZWQgaG93IHRoZXNlIHdvcmRzIGJyZWFrZG93bi4gQXJlIHRoZXJlIGFueSB0aGF0IEkgY291bGQgZGlzY291bnQgZnJvbSBteSBhbmFseXNpcyB1c2luZyB0aGUgc3RvcHdvcmRzIGxpc3QuIFdoaWNoIHdvcmRzIGFyZSB1c2VkIGluIGFidW5kYW5jZT8gV2hpY2ggYXJlIHVzZWQgc3BhcmluZ2x5PyBXaGljaCB3b3JkcyByZXBlYXQsIG5vdCBvbmx5IGFzIHRoZSBzYW1lIHdvcmQsIGJ1dCBhbHNvIGFzIGEgc3lub255bS4gSG93IG1hbnkgZGlmZmVyZW50IHdvcmRzIGRvZXMgUG9lIHVzZSB0byBkZXNjcmliZSB0aGUgYm9keSBvciBpbGxuZXNzPyBXaGF0IGFyZSB0aGUgdW5mYW1pbGlhciB3b3Jkcz8gQmVsb3cgSSB1c2UgUiB0byBjb3VudCB0aGUgd29yZHMgYW5kIHNvcnQgdG9wIDEwMCB1c2VkIHdvcmRzLg0KDQpgYGB7cn0NCnVzaGVyMSAlPiUgDQpjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBmaWx0ZXIobj44KSAlPiUgDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLG4pKSAlPiUgDQogIGdncGxvdChhZXMod29yZCxuKSkgKw0KICBnZW9tX2NvbChmaWxsPSJ0b21hdG8iKSsNCiAgeGxhYihOVUxMKSsNCiAgY29vcmRfZmxpcCgpKw0KICB0aGVtZV9mZXcoKQ0KYGBgDQpIb3BpbmcgdG8gc2VlIHRoZXNlIHdvcmRzIG1vcmUgY2xlYXJseSBvbiBhIHBsb3QsIEkgY2FsbCBmcmVxX3Rlcm1zIGZyb20gdGhlIHFkYXAgbGlicmFyeS4gVG8gYmVjb21lIGZhbWlsaWFyIHdpdGggdGhlIGxpYnJhcnksIEkgdHJ5IHRvIHBsb3Qgd29yZHMgdGhhdCBhdCBsZWFzdCBlaWdodCBjaGFyYWN0ZXJzIGFmdGVyIEkgZm91bmQgdGhlIHRvcCBmb3VyIGZyZXF1ZW50IHRlcm1zLg0KDQpgYGB7cn0NCmZyZXFfdGVybXModXNoZXIxLDI1KQ0KYGBgDQojc3RvcHdvcmRzDQoNClRoZXJlIGFyZSB0aHJlZSByZXNvdXJjZXMgUiBjYW4gY2FsbCB0byBtYW5hZ2Ugc3RvcHdvcmRzLiBPZnRlbiBwZW9wbGUgc2F5IHRoYXQgc29tZSB3b3JkcyBhcmUgbm90IGltcG9ydGFudCB3aGljaCBpcyByYXRoZXIgcHJlc3VtcHRpdmUuIERlcGVuZGluZyBvbiB3aGF0IGEgcGVyc29uIGlzIHRyeWluZyB0byB1bmRlcnN0YW5kIGFib3V0IGEgdGV4dCwgYWxsIHdvcmRzIGFyZSBpbXBvcnRhbnQgdW50aWwgdGhleSBhcmUgbm90LiBGb3IgaW5zdGFuY2UsIGluIFVzaGVyLCB3ZSBoYXZlIHRvIHVuZGVyc3RhbmQgdGhlIHN0b3J5IGlzIHRvbGQgaW4gdGhlIGZpcnN0IHBlcnNvbi4gVGhlIGNvbmNlcHQgb2YgJ0knIGlzIHZlcnkgaW1wb3J0YW50IHRvIHRoZSBtZWFuaW5nIG9mIHRoZSBzdG9yeS4gQmVmb3JlIEkgYXBwbHkgIHN0b3B3b3JkcywgSSByZXRhaW5lZCBwcm9ub3VucyBzdWNoIGFzIEkgYW5kIGhpcywgYnV0IGFmdGVyIEkgYXBwbGllZCB0aGUgc3RvcHdvcmRzLCBJIGhhZCB0byBjb25zaWRlciB0aGUgYW1vdW50IG9mIGRlcHRoIEkgbG9zZSBiZWNhdXNlIHByb25vdW5zIGFuZCB2ZXJiIHRlbnNlIHdhcyBleGVtcHQgZnJvbSBteSBhbmFseXNpcyBhZnRlciBydW5uaW5nIHRoYXQgY2h1bmsuDQoNCkFGSU5OIEZpbm4gQXJ1cCBOaWVsc2VuDQpiaW5nIEJpbmcgTGllIGFuZCBjb2xsYWJvcmF0b3JzDQpucmMgU2FpZiBNb2hhbW1lZCBhbmQgUHV0ZXIgVHVybmV5DQoNCmBgYHtyfQ0KdXNoZXIxICU+JSANCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSANCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpDQpgYGANCnFkYXAgaGFzIGEgZnVuIGZyZXF1ZW5jeSB0ZXJtcyBmdW5jdGlvbi4gQmVsb3cgSSBmb3VuZCBhbGwgdGhlIHdvcmRzIHdpdGggYXQgbGVhc3QgMTEgY2hhcmFjdGVycyAodG9rZW5zKSBhbmQgcGxvdHRlZCB0aGUgbnVtYmVyIG9mIHRpbWVzIHRoZXkgYXBwZWFyZWQgaW4gdGhlIHRleHQuIA0KDQoNCg0KYGBge3J9DQpmcmVxIDwtIGZyZXFfdGVybXModXNoZXIxJHdvcmQsYXQubGVhc3QgPTExLCBzdG9wd29yZHMgPSBzdG9wX3dvcmRzKQ0KcGxvdChmcmVxKQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgYW5hbHlzaXMgY29uc2lkZXJzIHRoZSBpbXBvcnRhbmNlIG9mIGdpdmluZyB2YWx1ZSB0byB3b3Jkcy4gSSBqb2luIHVzaGVyMSB3aXRoIHRoZSBhZmlubiBsZXhpY29uIHRvIGRlcml2ZSB0aGUgc2VudGltZW50cyBvZiB3b3JkcyBpbiB0aGUgdGV4dC4gSSBjaG9zZSB0aGUgdG9wIDMwIG9jY3VycmVuY2VzLlVzaW5nIHRoZSBzdG9wd29yZHMsIG15IGZpcnN0IHZpc3VhbGl6YXRpb24gbGlzdHMgdGVycm9yIGFzIHRoZSBtb3N0IGZyZXF1ZW50IHdvcmQgdW5kZXIgdGhlIHVtYnJlbGxhIG9mIG5lZ2F0aXZlIHNlbnRpbWVudC4NCg0KDQpgYGB7cn0NCnVzaGVyMSAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoJ2FmaW5uJykpICU+JSANCiAgYXJyYW5nZShkZXNjKHZhbHVlKSkgLT4gdXNoZXJfc2VudGltZW50DQpgYGANCmBgYHtyfQ0KdXNoZXIxICU+JSANCiAgIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoJ2FmaW5uJykpICU+JSANCiAgICAgZmlsdGVyKHZhbHVlID4gMCkgJT4lIA0KICAgY291bnQod29yZCwgIHZhbHVlLHNvcnQgPSBUUlVFKSAgLT4gdXNoZXJfbmVnYXRpdmUNCmBgYA0KDQpgYGB7cn0NCnVzaGVyMSAlPiUgDQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImFmaW5uIikpICU+JSANCiAgZmlsdGVyKHZhbHVlIDw9IDApICU+JSANCiAgY291bnQod29yZCwgdmFsdWUsIHNvcnQgPSBUUlVFKSU+JSANCiAgaGVhZCgzMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHJlb3JkZXIod29yZCwgbiksIG4sIGZpbGwgPSB2YWx1ZSkpICsgZ2VvbV9jb2woKSArIGNvb3JkX2ZsaXAoKSArDQogICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibGFjayIsIGhpZ2ggPSAib3JhbmdlIikrDQogIHlsYWIoIk51bWJlciBvZiBPY2N1cnJlbmNlcyIpICsNCiAgICAgIHhsYWIoIldvcmRzIikgKyBnZ3RpdGxlKCJUaGUgRmFsbCBvZiB0aGUgSG91c2Ugb2YgVXNoZXIgTmVnYXRpdmUgU2VudGltZW50IHVzaW5nIHN0b3B3b3JkcyIpDQpgYGANCkhvd2V2ZXIsIGlmIEkgZGlzcmVnYXJkIHRoZSBzdG9wd29yZHMsIHRoZSBtb3N0IHJlY3VycmluZyB3b3JkIGlzIG5vIGFuZCBieSBhIGNvbnNpZGVyYWJsZSBhbW91bnQuIA0KDQoNCmBgYHtyfQ0KdXNoZXIxICU+JSANCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYWZpbm4iKSkgJT4lIA0KICBmaWx0ZXIodmFsdWUgPD0gMCkgJT4lIA0KICBjb3VudCh3b3JkLCB2YWx1ZSwgc29ydCA9IFRSVUUpJT4lIA0KICBoZWFkKDMwKSAlPiUgDQogIGdncGxvdChhZXMocmVvcmRlcih3b3JkLCBuKSwgbiwgZmlsbCA9IHZhbHVlKSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmxhY2siLCBoaWdoID0gIm9yYW5nZSIpKw0KICB5bGFiKCJOdW1iZXIgb2YgT2NjdXJyZW5jZXMiKSArDQogICAgICB4bGFiKCJXb3JkcyIpICsgZ2d0aXRsZSgiVGhlIEZhbGwgb2YgdGhlIEhvdXNlIG9mIFVzaGVyIE5lZ2F0aXZlIFNlbnRpbWVudCBpbmNsdWRpbmcgc3RvcF93b3JkcyIpDQpgYGANCkNvbnZlcnNlbHksIGlmIHdlIGNvbnNpZGVyIHRoZSBwb3NpdGl2ZSB3b3JkcyB3ZSBleHBlcmllbmNlIHRoZSBzYW1lIGNvbnVuZHJ1bS4gSW1wbGVtZW50aW5nIHN0b3B3b3JkcyByZW1vdmVkIHdvcmRzIHN1Y2ggYXMgY2VydGFpbiwgZ29vZCwgYmVzdCwgYW5kIGdyZWF0Lg0KDQoNCmBgYHtyfQ0KdXNoZXIxICU+JSANCiAgIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoJ2FmaW5uJykpICU+JSANCiAgICAgZmlsdGVyKHZhbHVlID4gMCkgJT4lIA0KICAgY291bnQod29yZCwgIHZhbHVlLHNvcnQgPSBUUlVFKSAgLT4gdXNoZXJfcG9zaXRpdmUNCmBgYA0KYGBge3J9DQp1c2hlcl9wb3NpdGl2ZSAlPiUgDQogIGhlYWQoMzApICU+JSANCiAgZ2dwbG90KGFlcyhyZW9yZGVyKHdvcmQsIG4sKSxuLCBmaWxsID0gdmFsdWUpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmF5IiwgaGlnaCA9ICJvcmFuZ2UiKSt5bGFiKCJOdW1iZXIgb2YgT2NjdXJyZW5jZXMiKSArDQogICAgICB4bGFiKCJXb3JkcyIpICsgZ2d0aXRsZSgiVGhlIEZhbGwgb2YgdGhlIEhvdXNlIG9mIFVzaGVyIFBvc2l0aXZlIFNlbnRpbWVudCIpDQpgYGANCg0KYGBge3J9DQp1c2hlcjEgJT4lIA0KICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCdhZmlubicpKSAlPiUgDQogICAgIGZpbHRlcih2YWx1ZSA+IDApICU+JSANCiAgIGNvdW50KHdvcmQsICB2YWx1ZSxzb3J0ID0gVFJVRSkgIC0+IHVzaGVyX3Bvc2l0aXZlDQpgYGANCg0KDQpgYGB7cn0NCnVzaGVyX3Bvc2l0aXZlICU+JSANCiAgaGVhZCgzMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHJlb3JkZXIod29yZCwgbiwpLG4sIGZpbGwgPSB2YWx1ZSkpICsgZ2VvbV9jb2woKSArIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImdyYXkiLCBoaWdoID0gIm9yYW5nZSIpK3lsYWIoIk51bWJlciBvZiBPY2N1cnJlbmNlcyIpICsNCiAgICAgIHhsYWIoIldvcmRzIikgKyBnZ3RpdGxlKCJUaGUgRmFsbCBvZiB0aGUgSG91c2Ugb2YgVXNoZXIgUG9zaXRpdmUgU2VudGltZW50IikNCmBgYA0KI1dvcmRjbG91ZHMNCg0KVGhlIHdvcmRjbG91ZCBjYW4gYmUgYWNjb21wbGlzaGVkIHVzaW5nIGEgdmFyaWV0eSBvZiBtZXRob2RzLiBBcyB3aXRoIGFsbCB0aGluZ3MsIGludGVudCBpcyB1c2VmdWwuIFdoeSBnbyB0aHJvdWdoIHRoZSBlZmZvcnQgdG8gZGlzcGxheSBldmVyeSBhcyBzb21lIHNvcnQgb2YgZGVzaWduPyBGb3Igc29tZSwgaXQgbWlnaHQgYmUgYSB1c2VmdWwgd2F5IHRvIGV4dHJhY3QgbWV0YWRhdGEgZnJvbSBvdXIgb3duIHdvcmsuIFBlcmhhcHMgd2UgY2FuIHJlZmluZSBvdXIgd29yayBpZiBpdCBzZWVtcyB0b28gb2ZmIHRoZSBtYXJrIG9mIG91ciBpbnRlbmRlZCBtZXNzYWdlLiANCg0KdGhlIFdvcmxkY2xvdWQyIGxldHMgeW91IGhvdmVyIG92ZXIgdGhlIHdvcmQgYW5kIHNlZSBhIGNvdW50LiANCg0KYGBge3J9DQp3b3JkY2xvdWQyKGRhdGE9dXNoZXJfbmVnYXRpdmUpDQpgYGANCg0KVGltZSB0byB0dXJuIG15IGZvY3VzIHRvIHRoZSBvdGhlciB0ZXh0IG15IHByb2plY3QsIEthdGUgQ2hvcGluJ3MgdGhlIEF3YWtlbmluZywgdGhpcyB0ZXh0IGlzIGNhdGVnb3JpemVkIG9uIHRoZSBndXRlbmJlcmdfYm9va3NoZWxmIGFzICJCYW5uZWQgQm9va3MgTGlzdCBmcm9tIHRoZSBBbWVyaWNhbiBMaWJyYXJ5IEFzc29jaWF0aW9uIiBJIHRyaWVkIHRvIGZpbmQgaXQgdXNpbmcgdGhlIHRpdGxlIGJ1dCB3YXMgdW5hYmxlIHRvIGRvd25sb2FkIElEIDEwMC4NCg0KDQpgYGB7cn0NClZpZXcoZ3V0ZW5iZXJnX21ldGFkYXRhKQ0KZ3V0ZW5iZXJnX21ldGFkYXRhICU+JSANCiAgZmlsdGVyKHRpdGxlPT0iVGhlIEF3YWtlbmluZyIpDQpgYGANCkkgd2VudCB0byB0aGUgc2l0ZSB0byBzZWUgaWYgbWF5YmUgSSB3YXMgc3BlbGxpbmcgc29tZXRoaW5nIHdyb25nLiANCkkgZm91bmQgYSB2ZXJzaW9uIEkgY2FuIGRvd25sb2FkIGJ1dCB0aGUgc3RvcnkgaXMgcGFydCBvZiBhIGNvbGxlY3Rpb24uIEkgbmVlZCB0byBkbyBhIGNvbnNpZGVyYWJsZSBhbW91bnQgb2YgY2xlYW5pbmcgYmVmb3JlIEkgY2FuIHRyeSB0byB1c2UgdGhpcyB0ZXh0Lg0KYGBge3J9DQpndXRlbmJlcmdfZG93bmxvYWQoZ3V0ZW5iZXJnX2lkID0gMTYwKSAjIFRoZSBBd2FrZW5pbmcuDQphd2FrZW5pbmdfd29yZHMgPC0gZ3V0ZW5iZXJnX2Rvd25sb2FkKDE2MCwgbWV0YV9maWVsZHMgPSAidGl0bGUiKQ0Kdmlldyhhd2FrZW5pbmdfd29yZHMpDQpgYGANCg0KDQpgYGB7cn0NCnN0cihhd2FrZW5pbmdfd29yZHMpDQpgYGANCg0KIyBmdW5jdGlvbiB0byBjbGVhbiB0ZXh0DQoNCg0KYGBge3J9DQpnbGltcHNlKGF3YWtlbmluZ193b3JkcykNCmBgYA0KDQoNCmBgYHtyfQ0KaGVhZChhd2FrZW5pbmdfd29yZHMpDQpgYGANCmBgYHtyfQ0KYXdha2VuaW5nX2NsZWFuPC1hd2FrZW5pbmdfd29yZHMgJT4lIA0KICBzbGljZSg4MDpuKCkpICU+JSANCiAgZHJvcF9uYSh0ZXh0KSAlPiUgDQogdW5uZXN0X3Rva2Vucyh3b3JkLHRleHQpDQpoZWFkKGF3YWtlbmluZ19jbGVhbikNCmBgYA0KYGBge3J9DQphd2FrZW5pbmdfY2xlYW4gJT4lIA0KICBjb3VudCh3b3JkLHNvcnQgPSBUUlVFKQ0KYGBgDQpgYGB7cn0NCmF3YWtlbmluZ19jbGVhbiAlPiUgDQogIGNvdW50KHdvcmQsc29ydCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKG4+MjAwKSAlPiUgDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLG4pKSAlPiUgDQogIGdncGxvdChhZXMod29yZCxuKSkrDQogIGdlb21fY29sKGZpbGw9InNwcmluZ2dyZWVuMyIpKw0KICB4bGFiKE5VTEwpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lX2ZldygpDQpgYGANCg0KDQpgYGB7cn0NCmF3YWtlbmluZ19iaWdyYW1zIDwtIGF3YWtlbmluZ193b3JkcyAlPiUgDQogIGRyb3BfbmEodGV4dCkgJT4lIA0KICAjIG4gPSAyIGhlcmUgbWVhbnMgYmlncmFtcy4gV2UgY291bGQgYWxzbyBtYWtlIHRyaWdyYW1zIChuID0gMykgb3IgYW55IHR5cGUgb2Ygbi1ncmFtDQogIHVubmVzdF90b2tlbnMoYmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lIA0KICAjIEdldCByaWQgb2YgTkFzIGluIHRoZSBuZXcgYmlncmFtIGNvbHVtbg0KICBkcm9wX25hKGJpZ3JhbSkgJT4lIA0KICAjIFNwbGl0IHRoZSBiaWdyYW1zIGludG8gdHdvIHdvcmRzIHNvIHdlIGNhbiByZW1vdmUgc3RvcHdvcmRzDQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JSANCiAgZmlsdGVyKCF3b3JkMSAlaW4lIGMoInRob3UiLCAidGh5IiwgInRoaW5lIiwgImVudGVyIiwgImV4ZXVudCIsICJleGl0IiksDQogICAgICAgICAhd29yZDIgJWluJSBjKCJ0aG91IiwgInRoeSIsICJ0aGluZSIsICJlbnRlciIsICJleGV1bnQiLCAiZXhpdCIpKSAlPiUgDQogICMgUHV0IHRoZSB0d28gd29yZCBjb2x1bW5zIGJhY2sgdG9nZXRoZXINCiAgdW5pdGUoYmlncmFtLCB3b3JkMSwgd29yZDIsIHNlcCA9ICIgIikgJT4lIA0KYXJyYW5nZShiaWdyYW0pDQphd2FrZW5pbmdfYmlncmFtcw0KYGBgDQpgYGB7cn0NCnRvcF9iaWdyYW1zIDwtIGF3YWtlbmluZ19iaWdyYW1zICU+JSANCiAgIyBDb3VudCBhbGwgdGhlIGJpZ3JhbXMgaW4gZWFjaCBwbGF5DQogIGNvdW50KHRpdGxlLCBiaWdyYW0sIHNvcnQgPSBUUlVFKSAlPiUgDQogICMgS2VlcCB0b3AgMTUgaW4gZWFjaCBwbGF5DQogIGdyb3VwX2J5KHRpdGxlKSAlPiUgDQogIHRvcF9uKDE1KSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogICMgTWFrZSB0aGUgYmlncmFtcyBhbiBvcmRlcmVkIGZhY3RvciBzbyB0aGV5IHBsb3QgaW4gb3JkZXINCiAgbXV0YXRlKGJpZ3JhbSA9IGZjdF9pbm9yZGVyKGJpZ3JhbSkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QodG9wX2JpZ3JhbXMsIGFlcyh5ID0gZmN0X3JldihiaWdyYW0pLCB4ID0gbiwgZmlsbCA9IHRpdGxlKSkgKyANCiAgZ2VvbV9jb2woKSArIA0KICBndWlkZXMoZmlsbCA9ICJub25lIikgKw0KICBsYWJzKHkgPSAiQ291bnQiLCB4ID0gTlVMTCwgDQogICAgICAgdGl0bGUgPSAiMTUgbW9zdCBmcmVxdWVudCBiaWdyYW1zIFRoZSBBd2FrZW5pbmciKSArDQogIGZhY2V0X3dyYXAodmFycyh0aXRsZSksIHNjYWxlcyA9ICJmcmVlIikgKw0KICB0aGVtZV9idygpDQpgYGANCmBgYHtyfQ0KbG9nMig4KQ0KYGBgDQpgYGB7cn0NCjJeMw0KYGBgDQoNCg0KYGBge3J9DQpwcm9ub3VucyA8LSBjKCJoZSIsICJzaGUiKQ0KDQpiaWdyYW1faGVfc2hlX2NvdW50cyA8LSBhd2FrZW5pbmdfd29yZHMgJT4lDQogIGRyb3BfbmEodGV4dCkgJT4lIA0KICAjIFNwbGl0IGludG8gYmlncmFtcw0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQ0KICAjIEZpbmQgY291bnRzIG9mIGJpZ3JhbXMNCiAgY291bnQoYmlncmFtLCBzb3J0ID0gVFJVRSkgJT4lDQogICMgU3BsaXQgdGhlIGJpZ3JhbSBjb2x1bW4gaW50byB0d28gY29sdW1ucw0KICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lDQogICMgT25seSBjaG9vc2Ugcm93cyB3aGVyZSB0aGUgZmlyc3Qgd29yZCBpcyBoZSBvciBzaGUNCiAgZmlsdGVyKHdvcmQxICVpbiUgcHJvbm91bnMpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHd0ID0gbiwgc29ydCA9IFRSVUUpICU+JSANCiAgcmVuYW1lKHRvdGFsID0gbikNCg0Kd29yZF9yYXRpb3MgPC0gYmlncmFtX2hlX3NoZV9jb3VudHMgJT4lDQogICMgTG9vayBhdCBlYWNoIG9mIHRoZSBzZWNvbmQgd29yZHMNCiAgZ3JvdXBfYnkod29yZDIpICU+JQ0KICAjIE9ubHkgY2hvb3NlIHJvd3Mgd2hlcmUgdGhlIHNlY29uZCB3b3JkIGFwcGVhcnMgbW9yZSB0aGFuIDEwIHRpbWVzDQogIGZpbHRlcihzdW0odG90YWwpID4gMTApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogICMgU3ByZWFkIG91dCB0aGUgd29yZDEgY29sdW1uIHNvIHRoYXQgdGhlcmUncyBhIGNvbHVtbiBuYW1lZCAiaGUiIGFuZCBvbmUgbmFtZWQgInNoZSINCiAgc3ByZWFkKHdvcmQxLCB0b3RhbCwgZmlsbCA9IDApICU+JQ0KICAjIEFkZCAxIHRvIGVhY2ggbnVtYmVyIHNvIHRoYXQgbG9ncyB3b3JrIChqdXN0IGluIGNhc2UgYW55IGFyZSB6ZXJvKQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgfiguICsgMSkgLyBzdW0oLiArIDEpKSAlPiUNCiAgIyBDcmVhdGUgYSBuZXcgY29sdW1uIHRoYXQgaXMgdGhlIGxvZ2dlZCByYXRpbyBvZiB0aGUgc2hlIGNvdW50cyB0byBoZSBjb3VudHMNCiAgbXV0YXRlKGxvZ3JhdGlvID0gbG9nMihzaGUgLyBoZSkpICU+JQ0KICAjIFNvcnQgYnkgdGhhdCByYXRpbw0KICBhcnJhbmdlKGRlc2MobG9ncmF0aW8pKQ0KDQojIFJlYXJyYW5nZSB0aGlzIGRhdGEgc28gaXQncyBwbG90dGFibGUNCnBsb3Rfd29yZF9yYXRpb3MgPC0gd29yZF9yYXRpb3MgJT4lDQogICMgVGhpcyBnZXRzIHRoZSB3b3JkcyBpbiB0aGUgcmlnaHQgb3JkZXItLS13ZSB0YWtlIHRoZSBhYnNvbHV0ZSB2YWx1ZSwgc2VsZWN0DQogICMgb25seSByb3dzIHdoZXJlIHRoZSBsb2cgcmF0aW8gaXMgYmlnZ2VyIHRoYW4gMCwgYW5kIHRoZW4gdGFrZSB0aGUgdG9wIDE1IHdvcmRzDQogIG11dGF0ZShhYnNsb2dyYXRpbyA9IGFicyhsb2dyYXRpbykpICU+JQ0KICBncm91cF9ieShsb2dyYXRpbyA8IDApICU+JQ0KICB0b3BfbigxNSwgYWJzbG9ncmF0aW8pICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkMiwgbG9ncmF0aW8pKSANCg0KIyBGaW5hbGx5IHdlIHBsb3QgdGhpcw0KZ2dwbG90KHBsb3Rfd29yZF9yYXRpb3MsIGFlcyh5ID0gd29yZCwgeCA9IGxvZ3JhdGlvLCBjb2xvciA9IGxvZ3JhdGlvIDwgMCkpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gd29yZCwgeWVuZCA9IHdvcmQsDQogICAgICAgICAgICAgICAgICAgeCA9IDAsIHhlbmQgPSBsb2dyYXRpbyksIA0KICAgICAgICAgICAgICAgc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMy41KSArDQogIGxhYnMoeCA9ICJIb3cgbXVjaCBtb3JlL2xlc3MgbGlrZWx5IiwgeSA9IE5VTEwpICsNCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICIiLCBsYWJlbHMgPSBjKCJNb3JlICdzaGUnIiwgIk1vcmUgJ2hlJyIpKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoLTMsIDMpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiOHgiLCAiNHgiLCAiMngiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsICIyeCIsICI0eCIsICI4eCIpKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCmBgYA0KYGBge3J9DQphd2FrZW5pbmdfY2xlYW4gPC0gYXdha2VuaW5nX3dvcmRzICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgIyBTcGxpdCBpbnRvIHdvcmQgdG9rZW5zDQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lIA0KICAjIFJlbW92ZSBzdG9wIHdvcmRzIGFuZCBvbGQgdGltZXkgd29yZHMNCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSANCiAgZmlsdGVyKCF3b3JkICVpbiUgYygidGhvdSIsICJ0aHkiLCAiaGF1ZSIsICJ0aGVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgInRoaW5lIiwgImVudGVyIiwgImV4ZXVudCIsICJleGl0IikpDQoNCiMgSm9pbiB0aGUgc2VudGltZW50IGRpY3Rpb25hcnkgDQphd2FrZW5pbmdfc2VudGltZW50IDwtIGF3YWtlbmluZ19jbGVhbiAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkNCmF3YWtlbmluZ19zZW50aW1lbnQNCmBgYA0KDQpgYGB7cn0NCmF3YWtlbmluZ19zZW50aW1lbnRfcGxvdCA8LSBhd2FrZW5pbmdfc2VudGltZW50ICU+JSANCiAgY291bnQodGl0bGUsIHNlbnRpbWVudCkNCg0KZ2dwbG90KGF3YWtlbmluZ19zZW50aW1lbnRfcGxvdCwgYWVzKHggPSBzZW50aW1lbnQsIHkgPSBuLCBmaWxsID0gdGl0bGUsIGFscGhhID0gc2VudGltZW50KSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCkpICsNCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMC41LCAxKSkNCmBgYA0KDQpgYGB7cn0NCmF3YWtlbmluZ19zcGxpdF9pbnRvX2xpbmVzIDwtIGF3YWtlbmluZ19zZW50aW1lbnQgJT4lIA0KICAjIERpdmlkZSBsaW5lcyBpbnRvIGdyb3VwcyBvZiAxMDANCiAgbXV0YXRlKGxpbmUgPSByb3dfbnVtYmVyKCksDQogICAgICAgICBsaW5lX2NodW5rID0gbGluZSAlLyUgMTAwKSAlPiUgDQogICMgR2V0IGEgY291bnQgb2YgcG9zdGl2ZSBhbmQgbmVnYXRpdmUgd29yZHMgaW4gZWFjaCAxMDAtbGluZSBjaHVuay4NCiAgY291bnQodGl0bGUsIGxpbmVfY2h1bmssIHNlbnRpbWVudCkgJT4lIA0KICAjIENvbnZlcnQgdGhlIHNlbnRpbWVudCBjb2x1bW4gaW50byB0d28gY29sdW1ucyBuYW1lZCAicG9zaXRpdmUiIGFuZCAibmVnYXRpdmUiDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZW50aW1lbnQsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAjIENhbGN1bGF0ZSBuZXQgc2VudGltZW50DQogIG11dGF0ZShzZW50aW1lbnQgPSBwb3NpdGl2ZSAtIG5lZ2F0aXZlKQ0KDQpnZ3Bsb3QoYXdha2VuaW5nX3NwbGl0X2ludG9fbGluZXMsDQogICAgICAgYWVzKHggPSBsaW5lX2NodW5rLCB5ID0gc2VudGltZW50LCBmaWxsID0gc2VudGltZW50KSkgKw0KICBnZW9tX2NvbCgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIiwgZW5kID0gMC45KSArDQogIGZhY2V0X3dyYXAodmFycyh0aXRsZSksIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KSSB0cmllZCB0byBtZXJnZSBib3RoIGRhdGEgdGFibGVzIHRvZ2V0aGVyLiBIb3dldmVyLCB0aGUgY2hhbGxlbmdlIGlzIHRoYXQgSSBhbSBncmFiYmluZyBhIHBhcnQgb2YgdGhlIGNvbGxlY3Rpb24gZnJvbSB0aGUgdGV4dCBJIGRvbid0IHdhbnQuIA0KDQpgYGB7cn0NCkRUMV8yX2Z1bGwgPC0gbWVyZ2UoYXdha2VuaW5nX3NlbnRpbWVudCwgdXNoZXJfc2VudGltZW50LCBhbGwgPSBUUlVFKSAgICAgICAgICMgRnVsbCBqb2luDQpEVDFfMl9mdWxsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHJpbnQgZGF0YS50YWJsZQ0KYGBgDQojI0NvbmNsdXNpb25zDQoNClRoZSBGYWxsIG9mIHRoZSBIb3VzZSBvZiBVc2hlciBhbmQgVGhlIEF3YWtlbmluZyBhcmUgdmVyeSBkaWZmZXJlbnQgdGV4dHMgeWV0IHRoZXkgaGF2ZSBpbnRlcmVzdGluZyBzaW1pbGFyaXRpZXMuIEthdGUgQ2hvcGluIGluY29ycG9yYXRlcyBQb2UncyBzdHlsZSBpbiBoZXIgd3JpdGluZy4gSXQgaXMgZGlmZmljdWx0IHRvIGRldGVybWluZSBpZiB0aGVzZSBhcmUgdGhlIHNhbWUgd29yZHMgaW4gYm90aCB0ZXh0cywgaG93ZXZlci4gQmVjYXVzZSBLYXRlIENob3BpbidzIHdvcmsgaXMgcGFydCBvZiBhIGNvbGxlY3Rpb24sIHdlIG5lZWQgdG8gcGFyc2UgdGhhdCB0ZXh0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGZyb20gdGhlIG90aGVyIHRleHRzIGZvdW5kIGluIHRoZSBkb3dubG9hZC5JIGFkZGl0aW9uLCBJIG5vdGljZWQgdGhhdCBJIHdhcyB1bmFibGUgdG8gZHJhd24gYW55dGhpbmcgY29uY2x1c2l2ZSBhYm91dCB0aGUgdGV4dHMgZXhjZXB0IGZvciBjb3VudHMgb2Ygd29yZHMgYW5kIHRoZWlyIHNlbnRpbWVudHMuIEkgYWxzbyBiZWxpZXZlIGFzIGEgcmVzZWFyY2hlciwgaXQgYmVob292ZXMgbWUgdG8gY3JlYXRlIG15IG93biBsaXN0IG9mIHN0b3Agd29yZHMgc28gSSBoYXZlIGJldHRlciBjb250cm9sIG92ZXIgd2hhdCBJIHdhbnQgdG8gZmlsdGVyLiANCg==